﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using TestOnlineProject.Models.DataAccess;
using TestOnlineProject.Models.Entities;
using TestOnlineProject.Common;

namespace TestOnlineProject.Models.BusinessLogic
{
    public class TestBLO
    {
        TestDAO testDAO = new TestDAO();
        TestQuestionDAO testQuestionDAO = new TestQuestionDAO();
        QuestionDAO questionDAO = new QuestionDAO();
        UserTestDAO userTestDAO = new UserTestDAO();
        UserAnswerDAO userAnswerDAO = new UserAnswerDAO();
        QuestionTypeDAO questionTypeDAO = new QuestionTypeDAO();
        SectionDAO sectionDAO = new SectionDAO();
        SectionQuestionDAO sectionQuestionDAO = new SectionQuestionDAO();
        UserDAO userDAO = new UserDAO();
        ParticipantDAO participantDAO = new ParticipantDAO();

        public List<Test> ToList(ref List<NoModel> noList, int? page, string keyWord = "", string createdBy = "", int actualUser = 0, int subjectId = 0, string column = "", string sort = "")
        {
            try
            {

                // Search test by subjectid.
                var tests = testDAO.SelectBySubjectId(subjectId).Where(t => t.TestTypeId == 1).OrderByDescending(t => t.TestId).ToList();

                // User role is teachers, students.
                if (!HttpContext.Current.User.IsInRole("1"))
                {
                    tests = tests.Where(t => t.IsActivated == true).ToList();
                }

                // Search test by keywords.
                tests = tests.Where(s => s.Title.ToLower().Contains(keyWord.ToLower())).ToList();

                // Search by 'Created By'.
                if (createdBy == "")
                {
                    tests = tests.ToList();
                }
                if (createdBy == "mine")
                {
                    tests = tests.Where(t => t.UserId == int.Parse(HttpContext.Current.User.Identity.Name)).ToList();
                }
                if (createdBy == "admin")
                {
                    tests = tests.Where(q => (q.User.RoleId == 1)).ToList();
                }
                if (createdBy == "teachers")
                {
                    tests = tests.Where(q => (q.User.RoleId == 2)).ToList();
                }
                if (createdBy == "students")
                {
                    tests = tests.Where(q => (q.User.RoleId == 3)).ToList();
                }
                if (createdBy == "actualUser")
                {
                    tests = tests.Where(q => ((q.User.UserId == actualUser))).ToList();
                }

                // Sort by column.
                switch (column + sort)
                {
                    case "NoAsc":
                        tests = tests.Where(c => c.SubjectId == subjectId).OrderBy(c => c.TestId).ToList();
                        break;
                    case "NoDesc":
                        tests = tests.Where(c => c.SubjectId == subjectId).OrderByDescending(c => c.TestId).ToList();
                        break;
                    case "TitleAsc":
                        tests = tests.Where(c => c.SubjectId == subjectId).OrderBy(c => c.Title).ToList();
                        break;
                    case "TitleDesc":
                        tests = tests.Where(c => c.SubjectId == subjectId).OrderByDescending(c => c.Title).ToList();
                        break;
                    case "IsActivatedAsc":
                        tests = tests.Where(c => c.SubjectId == subjectId).OrderBy(c => c.IsActivated).ToList();
                        break;
                    case "IsActivatedDesc":
                        tests = tests.Where(p => p.SubjectId == subjectId).OrderByDescending(c => c.IsActivated).ToList();
                        break;
                    default:
                        break;
                }

                // Generate no. List.
                foreach (var t in tests)
                {
                    noList.Add(new NoModel() { Id = t.TestId });
                }
                noList = noList.OrderBy(n => n.Id).ToList();
                for (int i = 0; i < noList.Count; i++)
                {
                    noList[i].No = i + 1;
                }

                return tests;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public List<Test> ToList(int subjectId)
        {
            try
            {
                var list = testDAO.SelectBySubjectId(subjectId);
                return list;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public List<Test> ToList()
        {
            try
            {
                var list = testDAO.Select();
                return list;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public List<Test> Recommend()
        {
            try
            {
                var list = testDAO.Select().Where(t => t.IsActivated).OrderByDescending(t => t.TestId).ToList();
                if (!HttpContext.Current.User.IsInRole("1"))
                {
                    list = list.Where(t => t.Subject.Participants.Where(p => p.IsActivated && p.UserId == int.Parse(HttpContext.Current.User.Identity.Name)).ToList().Count > 0 && t.Subject.IsActivated).ToList();
                }
                return list;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public Test AddTest(CustomTestModel customTestModel)
        {
            try
            {
                var test = new Test();

                // Classic test.
                if (customTestModel.TestTypeId == 1)
                {
                    test.UserId = customTestModel.UserId;
                    test.SubjectId = customTestModel.SubjectId;
                    test.Title = customTestModel.Title;
                    test.Duration = customTestModel.Duration;
                    test.CreatedTime = DateTime.Now;
                    test.LastEditedTime = DateTime.Now;
                    test.Expires = DateTime.Parse(customTestModel.Expires);
                    test.IsActivated = true;
                    test.TestTypeId = customTestModel.TestTypeId;

                    // Insert test questions.
                    var questions = GenerateQuestionList(customTestModel);
                    for (int i = 0; i < questions.Count; i++)
                    {
                        var testQuestion = new TestQuestion() { QuestionId = questions[i].QuestionId, TestId = test.TestId };
                        test.TestQuestions.Add(testQuestion);
                    }

                    testDAO.Insert(test);
                }
                // Quick test.
                if (customTestModel.TestTypeId == 2)
                {
                    test.UserId = customTestModel.UserId;
                    test.SubjectId = customTestModel.SubjectId;
                    test.CreatedTime = DateTime.Now;
                    test.IsActivated = true;
                    test.TestTypeId = customTestModel.TestTypeId;
                    testDAO.Insert(test);
                }

                return test;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public int Modify(CustomTestModel customTestModel)
        {
            try
            {
                var test = testDAO.SelectByTestId(customTestModel.TestId);
                int result = 0;

                // Classic test.
                if (customTestModel.TestTypeId == 1 && customTestModel.LastEditedTime.ToString().Equals(test.LastEditedTime.ToString()))
                {
                    if (!CheckIsUsingTest(customTestModel.TestId))
                    {
                        test.Title = customTestModel.Title;
                        test.Duration = customTestModel.Duration;
                        test.Expires = DateTime.Parse(customTestModel.Expires);
                        test.LastEditedTime = DateTime.Now;
                        result = testDAO.Update(test);
                    }
                    else
                    {
                        // The Test is using.
                        result = -2;
                    }
                }
                else
                {
                    // out of date.
                    return -1;
                }

                return result;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public int Deactivate(string ids = "")
        {
            try
            {
                var spliter = new StringHelper();

                // Split id string to int array.
                int[] idInt = spliter.Split(ids);

                int result = 0;

                // Deactivate tests.
                foreach (var i in idInt)
                {
                    // Select test by id.
                    var test = testDAO.SelectByTestId(i);

                    if (HttpContext.Current.User.IsInRole("1"))
                    {

                        // Check test is activated?
                        //if (test.IsActivated)
                        //{
                        test.IsActivated = false;
                        result += testDAO.Update(test);
                        //}
                    }
                }

                return result;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public int Activate(string ids = "")
        {
            try
            {
                var spliter = new StringHelper();

                // Split id string to int array.
                int[] idInt = spliter.Split(ids);

                int result = 0;

                // Deactivate tests.
                foreach (var i in idInt)
                {
                    // Select test by id.
                    var test = testDAO.SelectByTestId(i);
                    if (HttpContext.Current.User.IsInRole("1"))
                    {
                        // Check test is activated?
                        //if (!test.IsActivated)
                        //{
                        test.IsActivated = true;
                        result += testDAO.Update(test);
                        //}
                    }
                }

                return result;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public int Remove(string ids = "")
        {
            try
            {
                var spliter = new StringHelper();

                // Split id string to int array.
                int[] idInt = spliter.Split(ids);

                int result = 0;

                // Delete tests.
                foreach (var i in idInt)
                {
                    // Select test by id.
                    var test = testDAO.SelectByTestId(i);
                    if (HttpContext.Current.User.IsInRole("1"))
                    {
                        // Check is using test.
                        if (!CheckIsUsingTest(i))
                        {
                            result = testDAO.Delete(i);
                        }
                        else
                        {
                            // the test is using.
                            result = -1;
                            break;
                        }
                    }
                    if (int.Parse(HttpContext.Current.User.Identity.Name) == test.UserId)
                    {
                        // Check is using test.
                        if (!CheckIsUsingTest(i))
                        {
                            var part = participantDAO.SelectByUserIdSubjectId(int.Parse(HttpContext.Current.User.Identity.Name), test.SubjectId);

                            if (part != null)
                            {
                                if (test.IsActivated && test.Subject.IsActivated && test.User.IsActivated && part.IsActivated)
                                {
                                    result = testDAO.Delete(i);
                                }
                            }
                        }
                        else
                        {
                            // the test is using.
                            result = -1;
                            break;
                        }
                    }
                }

                return result;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public bool CheckIsUsingTest(int testId)
        {
            try
            {
                var usertests = userTestDAO.SelectByTestId(testId);
                var result = false;
                foreach (var ut in usertests)
                {
                    if (ut.StartTime.AddMinutes(ut.Test.Duration + 3) > DateTime.Now && ut.FinishTime == null)
                    {
                        result = true;
                        break;
                    }
                }
                return result;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Add TestQuestion.
        /// </summary>
        /// <param name="testQuestion">TestQuestion object</param>
        /// <returns>Number of TestQuestion has been added</returns>

        private int AddTestQuestion(TestQuestion testQuestion)
        {
            try
            {
                int result = testQuestionDAO.Insert(testQuestion);
                return result;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public List<Question> GenerateQuestionList(CustomTestModel customTestModel)
        {
            try
            {
                List<Question> list = new List<Question>();

                // Get question from section.
                int[] questionIds = sectionQuestionDAO.SelectBySectionId(customTestModel.SectionId).Where(q => q.Question.IsActivated).Select(s => s.QuestionId).ToArray();
                var questions = questionDAO.SelectBySubjectId(customTestModel.SubjectId).Where(q => (q.IsActivated == true && questionIds.Contains(q.QuestionId))).ToList();

                var random = new Random();
                if (customTestModel.TestTypeId != 2)
                {
                    // Add to list.
                    if (questions.Count >= customTestModel.NumberOfQuestions)
                    {
                        // Get random list.
                        do
                        {
                            var question = questions.OrderBy(i => Guid.NewGuid()).FirstOrDefault();
                            list.Add(question);
                            questions.Remove(question);
                        } while (list.Count < customTestModel.NumberOfQuestions);
                    }
                    else
                    {
                        list.AddRange(questions);
                    }
                }
                else
                {
                    list.AddRange(questions);
                }
                return list;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public Test Find(int testId)
        {
            try
            {
                var test = testDAO.SelectByTestId(testId);
                return test;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public Test FindActivatedTest(int testId)
        {
            try
            {
                var test = testDAO.SelectByTestId(testId);
                if (HttpContext.Current.User.IsInRole("1"))
                {
                    return test;
                }
                if (test.IsActivated && test.Expires > DateTime.Now && test.TestQuestions.Where(tq => tq.Question.IsActivated).Count() > 0)
                {
                    return test;
                }
                return null;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public TestModel GenerateTestModel(int testId, int userTestId)
        {
            try
            {
                var testModel = new TestModel();
                var userTest = userTestDAO.SelectByUserTestId(userTestId);

                testModel.Title = userTest.Test.Title;
                testModel.userTestId = userTestId;
                testModel.StartTime = userTest.StartTime;
                testModel.Subject = userTest.Test.Subject.Name;

                // Get test question list.
                //var testQuestions = testQuestionDAO.SelectByTestId(testId);
                //List<Question> questions = new List<Question>();
                //foreach (var i in testQuestions)
                //{
                //    var question = questionDAO.SelectByQuestionId(i.QuestionId);
                //    questions.Add(question);
                //}

                List<Question> questions = new List<Question>();
                foreach (var userAnswer in userTest.UserAnswers)
                {
                    if (questions.Where(q => q.QuestionId == userAnswer.QuestionId).Count() == 0)
                    {
                        questions.Add(questionDAO.SelectByQuestionId(userAnswer.QuestionId));
                    }
                }

                // Generate QuestionModel list.
                List<QuestionModel> questionModels = new List<QuestionModel>();
                foreach (var q in questions)
                {
                    var questionModel = new QuestionModel();
                    questionModel = GenerateQuestionModel(q, 1, userTestId);
                    questionModels.Add(questionModel);
                }
                testModel.Questions = questionModels;
                testModel.NumberOfQuestions = questionModels.Count;

                return testModel;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public QuestionModel GenerateQuestionModel(Question q, int testTypeId, int userTestId = 0)
        {
            try
            {
                // TestModel contain QuestionModel.
                var questionModel = new QuestionModel();
                questionModel.Content = q.Content;
                questionModel.QuestionId = q.QuestionId;
                questionModel.QuestionTypeId = q.QuestionTypeId;
                questionModel.SubjectId = q.SubjectId;

                if (q.Explanation != null)
                {
                    questionModel.Explanation = q.Explanation;
                }
                else
                {
                    questionModel.Explanation = "";
                }

                var answers = new List<AnswerModel>();
                var matchs = new List<MatchModel>();

                if (testTypeId == 1)
                {
                    var useranswers = userAnswerDAO.SelectByUserTestIdQuestionId(userTestId, q.QuestionId);
                    // Add answers.
                    foreach (var useranswer in useranswers)
                    {
                        var answerModel = new AnswerModel() { AnswerId = useranswer.AnswerId, Content = useranswer.Answer.Content, Grade = useranswer.Answer.Grade };

                        // Load last saved states.
                        if (useranswer.IsSelected != null)
                        {
                            answerModel.Checked = useranswer.IsSelected;
                        }
                        if (useranswer.MatchId != null)
                        {
                            answerModel.MatchId = useranswer.MatchId.Value;
                        }

                        answers.Add(answerModel);

                    }
                }
                else
                {
                    foreach (var asd in q.Answers)
                    {
                        var answerModel = new AnswerModel()
                            {
                                AnswerId = asd.AnswerId,
                                Content = asd.Content,
                            };

                        // Load last saved states.
                        var userAnswer = userAnswerDAO.SelectByUserTestIdQuestionIdAnswerId(userTestId, asd.QuestionId, asd.AnswerId);
                        if (userAnswer != null)
                        {
                            answerModel.Checked = userAnswer.IsSelected;
                            answerModel.MatchId = userAnswer.MatchId.Value;
                        }
                        answers.Add(answerModel);
                    }

                    for (var i = 0; i < answers.Count; i++)
                    {
                        var random = new Random();
                        var pos = random.Next(answers.Count);
                        var temp = answers[i];
                        answers[i] = answers[pos];
                        answers[pos] = temp;
                    }
                }
                // Add matches.
                foreach (var m in q.Matches)
                {
                    var matchModel = new MatchModel() { MatchId = m.MatchId, Content = m.Content };
                    matchs.Add(matchModel);
                }

                questionModel.Answers = answers;
                questionModel.Matchs = matchs;

                return questionModel;
            }
            catch (Exception)
            {
                throw;
            }
        }


        //private List<Question> GenerateRandomList(List<Question> list)
        //{
        //    Random random = new Random();
        //    for (var i = 0; i < list.Count; i++)
        //    {
        //        var pos = random.Next(list.Count);
        //        var temp = list[i];
        //        list[i] = list[pos];
        //        list[pos] = temp;

        //    }
        //    return list;
        //}


        public int AddUserTest(int userId, int testId)
        {
            try
            {
                var test = testDAO.SelectByTestId(testId);

                if (HttpContext.Current.User.IsInRole("1") || (test.IsActivated == true && DateTime.Now < test.Expires))
                {
                    // Create user test session.
                    var userTest = new UserTest()
                    {
                        UserId = userId,
                        TestId = testId,
                        StartTime = DateTime.Now,
                        Mark = 0
                    };

                    int result = userTestDAO.Insert(userTest);

                    //
                    AddUserAnswersFirstTime(userTest.UserTestId);

                    return userTest.UserTestId;
                }
                else
                {
                    return 0;
                }
            }
            catch (Exception)
            {
                throw;
            }
        }


        private int AddUserAnswersFirstTime(int userTestId)
        {
            try
            {
                var userTest = userTestDAO.SelectByUserTestId(userTestId);
                List<UserAnswer> userAnswers = new List<UserAnswer>();

                // Random test questions.
                List<TestQuestion> testQuestions = userTest.Test.TestQuestions.ToList();
                var random = new Random();
                for (int i = 0; i < testQuestions.Count; i++)
                {
                    var pos = random.Next(testQuestions.Count);
                    var temp = testQuestions[i];
                    testQuestions[i] = testQuestions[pos];
                    testQuestions[pos] = temp;
                }

                foreach (var question in testQuestions)
                {
                    var randomUserAnswers = new List<UserAnswer>();

                    // Get useranswer of a question.
                    foreach (var answer in question.Question.Answers)
                    {
                        var userAnswer = new UserAnswer() { UserTestId = userTestId, QuestionId = answer.QuestionId, AnswerId = answer.AnswerId };
                        randomUserAnswers.Add(userAnswer);
                    }

                    // Random useranswer.
                    for (var i = 0; i < randomUserAnswers.Count; i++)
                    {
                        var pos = random.Next(randomUserAnswers.Count);
                        var temp = randomUserAnswers[i];
                        randomUserAnswers[i] = randomUserAnswers[pos];
                        randomUserAnswers[pos] = temp;
                    }

                    // Add useranswers to list.
                    foreach (var a in randomUserAnswers)
                    {
                        userAnswers.Add(a);
                    }
                }

                var result = userAnswerDAO.Insert(userAnswers);
                return result;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Generate UserAnswer.
        /// </summary>
        /// <param name="testModel"></param>
        /// <returns></returns>
        public int AddUserAnswers(TestModel testModel)
        {
            try
            {
                if (CheckIsAvailableUserTest(testModel.userTestId))
                {
                    // Generate answer list from TestModel.
                    List<UserAnswer> answerList = new List<UserAnswer>();
                    foreach (var q in testModel.Questions)
                    {
                        foreach (var a in q.Answers)
                        {
                            UserAnswer userAnswer = new UserAnswer();
                            userAnswer.UserTestId = testModel.userTestId;
                            userAnswer.QuestionId = q.QuestionId;
                            userAnswer.AnswerId = a.AnswerId;
                            userAnswer.IsSelected = a.Checked;
                            if (a.MatchId > 0)
                            {
                                userAnswer.MatchId = a.MatchId;
                                userAnswer.IsSelected = true;
                            }
                            answerList.Add(userAnswer);
                        }
                    }

                    // Insert answers to db.
                    var result = userAnswerDAO.Insert(answerList);

                    return result;
                }
                else
                {
                    // This user test not available.
                    return 0;
                }
            }
            catch (Exception)
            {
                throw;
            }
        }


        /// <summary>
        /// Check Is Available UserTest.
        /// </summary>
        /// <param name="userTestId"></param>
        /// <returns></returns>

        private bool CheckIsAvailableUserTest(int userTestId)
        {
            try
            {
                var userTest = userTestDAO.SelectByUserTestId(userTestId);
                if (userTest.Test.TestTypeId != 3)
                {
                    if (DateTime.Now < userTest.StartTime.AddMinutes(userTest.Test.Duration + 3) &&
                        userTest.FinishTime == null)
                    {
                        return true;
                    }
                }
                else
                {
                    if (userTest.FinishTime == null)
                    {
                        return true;
                    }
                }

                return false;
            }
            catch (Exception)
            {
                throw;
            }
        }


        /// <summary>
        /// Calculate grade.
        /// </summary>
        /// <param name="testModel"></param>
        /// <returns></returns>

        public void CalculateGrade(TestModel testModel)
        {
            try
            {
                double mark = 0;
                foreach (var question in testModel.Questions)
                {
                    question.Explanation = questionDAO.SelectByQuestionId(question.QuestionId).Explanation;
                    // Single choice, true/false.
                    if (question.QuestionTypeId == 1 || question.QuestionTypeId == 4)
                    {

                        // Get base valid answer.
                        var answerBase = questionDAO.SelectByQuestionId(question.QuestionId).Answers.Where(a => a.Grade == 100).FirstOrDefault();

                        // Calculate grade.
                        foreach (var answer in question.Answers)
                        {

                            if (answer.AnswerId == answerBase.AnswerId)
                            {

                                if (answer.Checked)
                                {
                                    answer.Grade = answerBase.Grade;
                                    mark += answerBase.Grade;
                                }
                                //answer.IsValid = true;
                            }
                            else
                            {
                                //answer.IsValid = false;
                            }
                        }
                    }

                    // Multiple choice.
                    if (question.QuestionTypeId == 2)
                    {

                        // Get base valid answer.
                        var answerBases = questionDAO.SelectByQuestionId(question.QuestionId).Answers.ToList();
                        double privateGrade = 0;

                        // Calculate grade.
                        foreach (var answer in question.Answers)
                        {
                            var ans = answerBases.Where(a => a.AnswerId == answer.AnswerId).FirstOrDefault();
                            if (answer.Checked)
                            {

                                if (ans.Grade > 0)
                                {
                                    privateGrade += ans.Grade;
                                    answer.Grade = ans.Grade;
                                }
                            }

                        }

                        if (privateGrade < 0)
                        {
                            privateGrade = 0;
                        }

                        mark += privateGrade;
                    }

                    // Matching.
                    if (question.QuestionTypeId == 3)
                    {

                        // Get base valid answer.
                        var matchingAnswerBases = questionDAO.SelectByQuestionId(question.QuestionId).Answers.OrderBy(a => a.AnswerId).ToList();
                        //double privateGrade = 0;

                        // Calculate grade.
                        foreach (var answer in question.Answers)
                        {
                            answer.Checked = true;

                            foreach (var ans in matchingAnswerBases)
                            {
                                if (answer.AnswerId == ans.AnswerId && answer.MatchId == ans.MatchId)
                                {
                                    //answer.IsValid = true;
                                    //mark += ans.Grade == null ? 0 : ans.Grade;
                                    mark += ans.Grade;
                                    answer.Grade = ans.Grade;
                                    break;
                                }
                            }
                        }
                    }
                }
                
                var userTest = userTestDAO.SelectByUserTestId(testModel.userTestId);
                if (userTest.FinishTime == null)
                {
                    userTest.FinishTime = DateTime.Now;
                    userTest.Mark = mark;
                    userTestDAO.Update(userTest);
                }
                //testModel.Title = userTest.Test.Title;
                //testModel.StartTime = userTest.StartTime;
                testModel.FinishTime = userTest.FinishTime.Value;
                System.TimeSpan duration = testModel.FinishTime.Subtract(testModel.StartTime);
                testModel.Duration = duration.Minutes;
                //testModel.NumberOfQuestions = userTest.Test.TestQuestions.Count;
                testModel.Mark = Math.Round(mark / 100, 2);

            }
            catch (Exception)
            {
                throw;
            }
        }


        public string ValidateCustomTest(CustomTestModel customTestModel)
        {
            try
            {
                var actualNumberOfQuestions = GenerateQuestionList(customTestModel).Count;
                if (actualNumberOfQuestions < customTestModel.NumberOfQuestions)
                {
                    return "Not enough questions to start the test. There are " + actualNumberOfQuestions + " questions has been found.";
                }
                return "valid";
            }
            catch (Exception)
            {
                throw;
            }
        }

        #region Quick Test

        /// <summary>
        /// Select one question from list Questions
        /// </summary>
        /// <param name="testModel">Id of Test object</param>
        /// <param name="sectionId">List Questions</param>
        /// <returns>Id of Question selected</returns>

        public int SelectOneQuestion(TestModel testModel, int sectionId)
        {
            try
            {
                var flag = true;
                var random = new Random();

                // Get list questions
                var sectionquestions = sectionQuestionDAO.SelectBySectionId(sectionId);

                // Shuffle list Question
                var randomQuestion = ShuffleListQuestions(sectionquestions);

                // Random index
                var index = random.Next(sectionquestions.Count);

                // Select Question
                if (testModel.Questions.Count > 0)
                {
                    do
                    {
                        foreach (var question in testModel.Questions)
                        {
                            if (randomQuestion[index].QuestionId == question.QuestionId)
                            {
                                flag = false;
                                index = random.Next(sectionquestions.Count);
                                break;
                            }
                            flag = true;
                        }
                    } while (!flag);
                }

                return randomQuestion[index].QuestionId;
            }
            catch (Exception)
            {
                return 0;
            }
        }

        /// <summary>
        /// Shuffle list Questions
        /// </summary>
        /// <param name="questions">List Question</param>
        /// <returns>List Question has been shuffled</returns>

        public List<SectionQuestion> ShuffleListQuestions(List<SectionQuestion> questions)
        {
            var random = new Random();
            for (var i = 0; i < questions.Count; i++)
            {
                var pos = random.Next(questions.Count);
                var temp = questions[i];
                questions[i] = questions[pos];
                questions[pos] = temp;
            }
            return questions;
        }

        /// <summary>
        /// Generate TestModel for Quick Test
        /// </summary>
        /// <param name="userTestId">Id of UserTest</param>
        /// <returns>TestModel object</returns>

        public TestModel GenerateTestModel(int userTestId)
        {
            try
            {
                // Get UserTest
                var userTest = userTestDAO.SelectByUserTestId(userTestId);

                // Generate TestModel
                var testModel = new TestModel
                    {
                        Questions = new List<QuestionModel>(),
                        userTestId = userTestId,
                        StartTime = userTest.StartTime,
                    };
                if (userTest.Mark != null)
                    testModel.Mark = userTest.Mark.Value;

                return testModel;
            }
            catch (Exception)
            {
                return null;
            }
        }

        /// <summary>
        /// Add Question Model to TestModel
        /// </summary>
        /// <param name="testModel">TestModel object</param>
        /// <param name="questionId">Id of Question</param>
        /// <returns>TestModel object</returns>

        public TestModel AddQuestionModel(TestModel testModel, int questionId)
        {
            // Get Question
            var question = questionDAO.SelectByQuestionId(questionId);

            //var result = AddUserAnswersFirstTime(question, testModel.userTestId);

            // Generate QuestioModel from Question
            var questionModel = GenerateQuestionModel(question, testModel.userTestId);

            // Add QuestionModel to TestModel
            testModel.Questions.Add(questionModel);

            return testModel;
        }

        public int AddUserAnswersFirstTime(Question question, int userTestId)
        {
            var userAnswers = new List<UserAnswer>();
            var randomUserAnswers = new List<UserAnswer>();
            var random = new Random();

            // Get useranswer of a question.
            foreach (var answer in question.Answers)
            {
                var userAnswer = new UserAnswer
                    {
                        UserTestId = userTestId,
                        QuestionId = answer.QuestionId,
                        AnswerId = answer.AnswerId
                    };
                randomUserAnswers.Add(userAnswer);
            }

            // Random useranswer.
            for (var i = 0; i < randomUserAnswers.Count; i++)
            {
                var pos = random.Next(randomUserAnswers.Count);
                var temp = randomUserAnswers[i];
                randomUserAnswers[i] = randomUserAnswers[pos];
                randomUserAnswers[pos] = temp;
            }

            // Add useranswers to list.
            foreach (var a in randomUserAnswers)
            {
                userAnswers.Add(a);
            }

            // Insert to db
            var result = userAnswerDAO.Insert(userAnswers);

            return result;
        }

        /// <summary>
        /// Calculate one by one Question
        /// </summary>
        /// <param name="testModel">TestModel object</param>
        /// <param name="testTypeId">Id of TestType</param>
        /// <param name="currentQuestion">Current Question</param>
        /// <returns>TestModel object</returns>

        public TestModel CalculateGrade(TestModel testModel, int currentQuestion)
        {
            // Calculate mark of Question
            var questionModel = CalculateGradeOneQuestion(testModel.Questions[currentQuestion]);

            // Sum mark of Question with mark of TestModel
            var mark = testModel.Mark + questionModel.Mark;

            // Update mark to DB
            var userTest = userTestDAO.SelectByUserTestId(testModel.userTestId);
            userTest.Mark = mark;
            userTestDAO.Update(userTest, "Not Finish");

            // Add value TestModel
            testModel.StartTime = userTest.StartTime;
            testModel.Mark = mark;

            return testModel;
        }

        /// <summary>
        /// Calculate mark of Question
        /// </summary>
        /// <param name="questionModel">QuestionModel object</param>
        /// <returns>QuestionModel object</returns>

        private QuestionModel CalculateGradeOneQuestion(QuestionModel questionModel)
        {
            // Single choice - True/False.
            if (questionModel.QuestionTypeId == 1 || questionModel.QuestionTypeId == 4)
            {
                // Get base valid answer.
                var answerBase =
                    questionDAO.SelectByQuestionId(questionModel.QuestionId).Answers.FirstOrDefault(a => a.Grade == 100);

                // Calculate grade.
                foreach (var answer in questionModel.Answers)
                {
                    if (answer.AnswerId == answerBase.AnswerId)
                    {
                        answer.Grade = answerBase.Grade;
                        if (answer.Checked)
                        {
                            questionModel.Mark += answerBase.Grade;
                        }
                    }
                }
            }

            // Multiple choice.
            if (questionModel.QuestionTypeId == 2)
            {
                // Get base valid answer.
                var answerBases = questionDAO.SelectByQuestionId(questionModel.QuestionId).Answers.ToList();
                double privateGrade = 0;

                // Calculate grade.
                foreach (var answer in questionModel.Answers)
                {
                    var ans = answerBases.FirstOrDefault(a => a.AnswerId == answer.AnswerId);
                    if (answer.Checked)
                    {
                        if (ans.Grade > 0)
                        {
                            answer.Grade = ans.Grade;
                            privateGrade += ans.Grade;
                        }
                        if (ans.Grade < 0)
                        {
                            answer.Grade = ans.Grade;
                            privateGrade += ans.Grade;
                        }
                    }
                }

                if (privateGrade < 0)
                {
                    privateGrade = 0;
                }
                if (privateGrade >= 99)
                {
                    privateGrade = 100;
                }
                questionModel.Mark += privateGrade;
            }

            // Matching.
            if (questionModel.QuestionTypeId == 3)
            {
                // Get base valid answer.
                var matchingAnswerBases =
                    questionDAO.SelectByQuestionId(questionModel.QuestionId).Answers.OrderBy(a => a.AnswerId).ToList();

                // Calculate grade.
                foreach (var answer in questionModel.Answers)
                {
                    answer.Checked = true;
                    foreach (var ans in matchingAnswerBases)
                    {
                        answer.Grade = ans.Grade;
                        if (answer.AnswerId == ans.AnswerId && answer.MatchId == ans.MatchId)
                        {
                            questionModel.Mark += ans.Grade;
                            break;
                        }
                    }
                }
            }
            return questionModel;
        }

        /// <summary>
        /// Finish Quick Test
        /// </summary>
        /// <param name="testModel">TestModel object</param>
        /// <returns>TestModel object</returns>

        public TestModel FinishQuickTest(TestModel testModel)
        {
            try
            {
                //var finishTime = DateTime.Now;
                //var userTest = userTestDAO.SelectByUserTestId(testModel.userTestId);
                //userTest.FinishTime = finishTime;
                //userTest.Mark = testModel.Mark;
                //var result = userTestDAO.Update(userTest);
                //if (result <= 0)
                //{
                //    return null;
                //}

                //var duration = finishTime.Subtract(userTest.StartTime).Minutes;

                //// Update TestModel
                //testModel.FinishTime = finishTime;
                //testModel.Duration = duration;
                //return result <= 0 ? null : testModel;

                var userTest = userTestDAO.SelectByUserTestId(testModel.userTestId);
                var finishTime = new DateTime();
                if (userTest.FinishTime != null)
                {
                    finishTime = (DateTime)userTest.FinishTime;
                    testModel.FinishTime = finishTime;
                }

                var duration = finishTime.Subtract(userTest.StartTime).Minutes;
                testModel.Duration = duration;

                return testModel;
            }
            catch (Exception)
            {
                return null;
            }
        }

        /// <summary>
        /// Add UserAnswer
        /// </summary>
        /// <param name="testModel">TestModel object</param>
        /// <param name="currentQuestion">number of current Question</param>
        /// <returns>Number of UserAnswer has been added</returns>

        public int AddUserAnswer(TestModel testModel, int currentQuestion)
        {
            try
            {
                var question = testModel.Questions[currentQuestion];
                var answerList = new List<UserAnswer>();
                foreach (var answer in question.Answers.Where(i => (i.Checked || i.MatchId > 0)))
                {
                    var userAnswer = new UserAnswer
                    {
                        UserTestId = testModel.userTestId,
                        QuestionId = question.QuestionId,
                        AnswerId = answer.AnswerId
                    };
                    if (answer.MatchId > 0)
                    {
                        userAnswer.MatchId = answer.MatchId;
                    }
                    answerList.Add(userAnswer);
                }
                var result = userAnswerDAO.Insert(answerList, "QuickTest");
                return result;
            }
            catch (Exception)
            {
                return 0;
            }
        }

        public int FinishQuickTest(int userTestId)
        {
            try
            {
                var finishTime = DateTime.Now;
                var userTest = userTestDAO.SelectByUserTestId(userTestId);
                userTest.FinishTime = finishTime;
                var result = userTestDAO.Update(userTest);
                if (result <= 0)
                {
                    return 0;
                }
                return result;
            }
            catch (Exception)
            {
                return 0;
            }
        }

        #endregion

        public int[] CreateChart(int id)
        {
            try
            {
                // select questions in test
                var questionPerTest = testQuestionDAO.SelectByTestId(id);
                // select list users take the test
                var tests = userTestDAO.SelectByTestId(id);
                var arrayMark = new int[11];

                if (tests.Count() != 0)
                {
                    foreach (var userTest in tests)
                    {
                        if (userTest.Mark >= 0 && userTest.Mark != null)
                        {
                            //var mark = (int)Math.Round((double)(userTest.Mark / (questionPerTest.Count() * 10)), MidpointRounding.AwayFromZero);
                            var intNum = (int)Math.Round((double)(userTest.Mark / (questionPerTest.Count() * 10)));
                            var mark = Math.Round((double)(userTest.Mark / (questionPerTest.Count() * 10)), 2) - intNum >= 0.5 ? intNum + 1 : intNum;
                            if (mark >= 0 && mark <= 10)
                            {
                                arrayMark[mark]++;
                            }
                        }
                    }
                    return arrayMark;
                }
                return null;
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}